/// file: include/arch/pci.h
// autor: jiangxinpeng
// time: 2021.10.16
// copyright: (C) 2020-2050 by jiangxinpeng,All right are reserved.

#ifndef ARCH_PCI_H
#define ARCH_PCI_H

#include <lib/type.h>

// define pci config space io register MICRO
#define PCI_CONFIG_ADDRESS 0xCF8
#define PCI_CONFIG_DATA 0xCFC

// define pci bus device function num MAX
#define PCI_DEV_MAX 31
#define PCI_BUS_MAX 255
#define PCI_FUN_MAX 7
#define PCI_DEVICE_MAX 255
#define PCI_BAR_MAX 6

// pci  bar size
#define PCI_BAR_SIZE 4

// is first pci bus
#define PCI_BUS_FIRST 0

// define pci config space address register filed format
#define PCI_CONFIG_ADDRESS_ENBIT_OFF 31
#define PCI_CONFIG_ADDRESS_RESERVED_OFF 24
#define PCI_CONFIG_ADDRESS_BUSNUM_OFF 16
#define PCI_CONFIG_ADDRESS_DEVICENUM_OFF 11
#define PCI_CONFIG_ADDRESS_FUNCTIONNUM_OFF 8
#define PCI_CONFIG_ADDRESS_REGOFF_OFF 2

// pci host controller type
#define PCI_SINGLEHOST 0 // single host
#define PCI_MUITIHOST 1  // multiple host

// pci flags
#define PCI_INIT 0  // init doing
#define PCI_READY 1 // init success

// devicd flags
#define PCI_DEVICE_INVALID 0 // device error
#define PCI_DEVICE_USING 1   // device in using

// bus flags
#define PCI_BUS_INVALID 0
#define PCI_BUS_USING 1

// bar flags
#define PCI_BAR_INVALID 0
#define PCI_BAR_AVAILABLE 1

// bar type
#define PCI_BAR_MEMMAP 0
#define PCI_BAR_IOMAP 1

// bar mask
#define PCI_MEMMAP_MASK (~0xf)
#define PCI_IOMAP_MASK (~0x3)

// pci address register reg offset low 2bits always is 0，in order to aligned all address to 32bits
#define PCI_CONFIG_ADDRESS_REGOFF_MASK 0xfc
#define PCI_NOEXITS_DEVICE 0xffff

// pci header type register filed mask
#define PCI_HEADERTYPE_MULITIFUN 0x80
#define PCI_HEADERTYPE_COMMON 0x00
#define PCI_HEADERTYPE_PCI_TO_PCIBRIDGE 0x01
#define PCI_HEADERTYPE_PCI_TO_CADRBUS_BRIDGE 0x02

// pci bist filed mask
#define PCI_BIST_CAPABLE 0x80
#define PCI_BIST_START 0x40
#define PCI_BIST_COMPLETIONCODE 0x0

// pci command filed mask
#define PCI_CMD_DISINTTERRUPT 0x0400         // disable interrupt
#define PCI_CMD_FASTBACKTOBACK_ENABLE 0x0200 // enable fast back to back
#define PCI_CMD_SERR_ENABLE 0x0100           // enable SERR
#define PCI_CMD_WAIT 0x80                    // enable address/data stepping
#define PCI_CMD_PARITY 0x40                  // enable parity checking
#define PCI_CMD_VGA_PALETTE 0x20             // enable palette snooping
#define PCI_CMD_INVALIDATE 0x10              // use memory write and invalidate
#define PCI_CMD_SPECIAL 0x8                  // enalbe response to special cycles
#define PCI_CMD_MASTER 0x4                   // enable bus master
#define PCI_CMD_MEMORY 0x2                   // enable response in memory space
#define PCI_CMD_IO 0x01                      // enable response in I/O space

// pci class code
#define PCI_CLASS_MASTSTOAGECTRL 0x01

// pci subclass code
#define PCI_SUBCLASS_IDECTRL 0x01

// pci config space
#define PCI_DEVICE_VENDOR 0x00
#define PCI_STATUS_COMMAND 0x04
#define PCI_CLASS_CODE_REVISION_ID 0x08
#define PCI_BASEADDR0 0x10
#define PCI_BASEADDR1 0x14
#define PCI_BASEADDR2 0x18
#define PCI_BASEADDR3 0x1C
#define PCI_BASEADDR4 0x20
#define PCI_BASEADDR5 0x24
#define PCI_CARDBUS_POINT 0x28
#define PCI_IRQ 0x3c
#define PCI_REVISION_ID 0x8
#define PCI_DEVICE 0x2
#define PCI_VENDOR 0x0
#define PCI_COMMAND 0x04
#define PCI_STATUS 0x02
#define PCI_SUBCLASS 0xA
#define PCI_CLASS 0xB
#define PCI_HEADTYPE 0x0E
#define PCI_BIST 0x0F
#define PCI_BAR0 0x10 // pci bar start

typedef struct
{
    uint8_t flags;
    uint8_t type;
    uint32_t len;
    uint32_t base;
} pci_bar_t;

typedef struct pci_bus
{
    uint8_t flags;

    int father; // father bus
    int child;  // child bus
    int bright; // bus bright

    int bus; // itselt bus number
} pci_bus_t;

typedef struct pci_dev
{
    int flags;

    uint8_t bus;
    uint8_t dev;
    uint8_t fun;
    uint8_t class;
    uint8_t subclass;
    uint8_t proIF;

    uint8_t header; // device header

    uint16_t vendorID;  // vendor id
    uint16_t deviceID;  // device id
    uint8_t revisionID; // revision id

    uint8_t muitifun; // muitifun flags
    uint32_t irq;     // irq number

    pci_bar_t bar[PCI_BAR_MAX]; // bar register
} pci_dev_t;

typedef struct pci_info
{
    uint8_t flags;                    // pci init flags
    uint8_t type;                     // pci controller type (single/muiti host)
    pci_dev_t device[PCI_DEVICE_MAX]; // pci device info
    pci_bus_t bus[PCI_BUS_MAX];       // pci bus info
    uint32_t bus_num;                 // pci bus number
    uint32_t dev_num;                 // pci device number
} pci_info_t;

extern pci_info_t pci;

// pci io
// pci device info operator
void PciInit();
void PciDeviceAdd(uint8_t bus, uint8_t dev, uint8_t fun);
void PciDeviceRemove(pci_dev_t *device);
pci_dev_t *PciGetDevice(uint32_t vendor_id, uint32_t devide_id);
pci_dev_t *PciGetDeviceByClass(uint32_t class, uint32_t subclass);
pci_dev_t *PciGetDeviceByClassAndProIF(uint32_t class, uint32_t subclass, uint32_t proIF);
uint32_t PciDeviceRead(pci_dev_t *device, uint32_t reg);
void PciDeviceWrite(pci_dev_t *device, uint32_t reg, uint32_t data);
uint32_t PciDeviceGetIoAddr(pci_dev_t *device);
uint32_t PciDeviceGetIrq(pci_dev_t *device);
uint32_t PciDeviceGetMemLen(pci_dev_t *device);
uint32_t PciDeviceGetMemAddr(pci_dev_t *device);
void PciEnableBusMaster(pci_dev_t *device);
void PciBusAdd(int father, int child, int isbright, uint8_t bus);
void PciBusRemove(pci_bus_t *bus);
void PciDump();

#endif